/*
 * PositioningSystem.cpp
 *
 *  Created on: Jan 1, 2011
 *      Author: Kevin Mueller
 */

#include "PositioningSystem.h"

#include "Main.h"
#include "Settings.h"

#include <QObject>
#include <QGeoPositionInfoSource>
#include <QDebug>

using namespace QtMobility;

PositioningSystem::PositioningSystem(QObject *parent) :
	QThread(parent) {
	source = QGeoPositionInfoSource::createDefaultSource(this);

	// TODO: Error-handling if source isn't available

	m = NULL;
	enabled = false;
	isMoving = false;
	currentSegment = NULL;
}

PositioningSystem::~PositioningSystem() {
	terminate();
	delete m;
	if (source)
		source->stopUpdates();
	wait();
}

void PositioningSystem::init() {
	if (source) {
		source->setPreferredPositioningMethods(
				QtMobility::QGeoPositionInfoSource::AllPositioningMethods);

		normalMode();

		Settings& s = Main::getInstance().getSettings();
		s.getGPSStatus()->getCurrent() == GPSStatus::ENABLED ? this->enable()
				: this->disable();

		connect	(s.getPollingInterval(), SIGNAL(settingChanged(AbstractSetting *)), this, SLOT(setInterval(AbstractSetting *)));
	}
}

void PositioningSystem::setInterval(AbstractSetting *s) {
	setInterval(s->getCurrent(), SECOND);
}

void PositioningSystem::setInterval(int v, TimeUnit unit) {
	if (source) {
		switch (unit) {
		case SECOND:
			v *= 1000;
			break;
		case MILLISECOND:
			break;
		}
		source->setUpdateInterval(v);
	}
}

void PositioningSystem::enable() {
	if (source) {
		connect(source, SIGNAL(positionUpdated(const QGeoPositionInfo&)), this, SLOT(positionUpdated(const QGeoPositionInfo&)));
		source->startUpdates();
		enabled = true;

		Main::getInstance().getSettings().getGPSStatus()->setCurrent(
				GPSStatus::ENABLED);

		start(QThread::IdlePriority);
	}
}

void PositioningSystem::disable() {
	if (source) {
		source->stopUpdates();
		disconnect(source, SIGNAL(positionUpdated(const QGeoPositionInfo&)), this, SLOT(positionUpdated(const QGeoPositionInfo&)));

		enabled = false;

		delete m;
		m = NULL;

		emit updated(m);
		emit stoppedMoving();

		Main::getInstance().getSettings().getGPSStatus()->setCurrent(
				GPSStatus::DISABLED);

		mutex.lock();
		threadRunning = false;
		delete currentSegment;
		currentSegment = NULL;
		mutex.unlock();
	}
}

void PositioningSystem::toggle() {
	isEnabled() ? disable() : enable();
}

bool PositioningSystem::isEnabled() {
	return enabled;
}

QGeoPositionInfo PositioningSystem::getLastPosition() {
	QGeoPositionInfo geo = source->lastKnownPosition(false);
	return geo;
	//	return ExtendedGeoPositionInfo(geo, this);
}

void PositioningSystem::positionUpdated(const QGeoPositionInfo &info) {
	if (info.isValid()) {
		ExtendedGeoPositionInfo *tmp = m;
		m = new ExtendedGeoPositionInfo(info, this);

		if(!isMoving && Main::getInstance().isMoving()){
			isMoving = true;
			emit startedMoving();
		}
		else if(isMoving && !Main::getInstance().isMoving()){
			isMoving = false;
			emit stoppedMoving();
		}

		emit updated(m);

		delete tmp;
	}
}

void PositioningSystem::normalMode() {
	AbstractSetting* s =
			Main::getInstance().getSettings().getPollingInterval();
	setInterval(s->getCurrent(), SECOND);
}

void PositioningSystem::powerSave() {
	Settings& s = Main::getInstance().getSettings();
	if (s.getReducedPollingInterval()->getCurrent() != 0) {
		setInterval(s.getReducedPollingInterval()->getCurrent(), SECOND);
	} else {
		setInterval(s.getPollingInterval()->getCurrent(), SECOND);
	}
}

void PositioningSystem::run(){
	threadRunning = true;
	while(threadRunning){
		if(isEnabled()){
			const QGeoPositionInfo last = getLastPosition();
			opennav::Coordinate coord(last.coordinate().longitude(), last.coordinate().latitude());
			mutex.lock();

			opennav::RoadSegment* tmp = currentSegment;
			currentSegment = new opennav::RoadSegment;

			if(currentSegment){
				opennav::Road road;
				opennav::RoadJunction junction;
				opennav::Coordinate c;

				if(!Main::getInstance().getMap().reverse_geocode(coord, road, *currentSegment, junction, c)){
					delete currentSegment;
					currentSegment = NULL;
				}
			}
			delete tmp;

			mutex.unlock();
		}
		sleep(5000);
	}
}


ExtendedGeoPositionInfo::ExtendedGeoPositionInfo(
		const QGeoPositionInfo &current, QObject *parent) :
	QObject(parent), QGeoPositionInfo(current) {

	AbstractSetting* s = Main::getInstance().getSettings().getUnit();
	setUnitSystem(s);

	connect(s, SIGNAL(settingChanged(AbstractSetting *)), this, SLOT(setUnitSystem(AbstractSetting *)));
}

ExtendedGeoPositionInfo::~ExtendedGeoPositionInfo() {

}

void ExtendedGeoPositionInfo::setUnitSystem(int s) {
	system = s;
}

void ExtendedGeoPositionInfo::setUnitSystem(AbstractSetting *s) {
	system = s->getCurrent();
}

int ExtendedGeoPositionInfo::getUnitSystem() const {
	return system;
}

qreal ExtendedGeoPositionInfo::getVelocity() const {
	qreal speed = attribute(QGeoPositionInfo::GroundSpeed);

	if (speed < 0)
		return 0.0;

	switch (system) {
	case Unit::MS:
		speed /= 3.6;
		break;
	case Unit::KMH:
		break;
	case Unit::MH:
		speed /= 1.6;
		break;
	}
	return speed;
}

qreal ExtendedGeoPositionInfo::getMovingDirection() const {
	return attribute(QGeoPositionInfo::Direction);
}

qreal ExtendedGeoPositionInfo::getDistance() const {
	// TODO: implementieren!
	return 0.0;
}

QGeoCoordinate::CoordinateType ExtendedGeoPositionInfo::getFixType() const {
	return coordinate().type();
}

const opennav::RoadSegment* ExtendedGeoPositionInfo::getRoadSegment() const{
	PositioningSystem& pos = Main::getInstance().getPositioningSystem();
	pos.mutex.lock();
	// FIXME: we still have a running condition present, if this function returns the current pointer to the current segment
	// after releasing the mutex the positioning thread will recalculate the current position and delete the pointer that was returned by this function
	// thus a running condition is possible - maybe solvable with a 2 elements ring buffer in positioning or something else?!
	const opennav::RoadSegment* ret = pos.currentSegment;
	pos.mutex.unlock();
	return ret;
}

QString ExtendedGeoPositionInfo::getRoadName() const{
	const opennav::RoadSegment* segment = getRoadSegment();
	QString ret = "";
	if(segment != NULL){
		opennav::Road road;
		Main::getInstance().getMap().get_road(*segment, road);

		if(road.labels.size()>0){
			for(register unsigned int i = 0 ; i < road.labels.size() - 1 ; i++){
				ret += QString(road.labels.at(i).c_str()).append(" - ");
			}
			ret += road.labels.at(road.labels.size()-1).c_str();
		}
	}
	return ret;
}
